/*
  Name:        PixelMap.cpp
  Copyright:   Version 0.1
  Author:      Rodrigo Luis de Souza da Silva
  Date:        04/07/2009
  Description: Simple class to load BMP images and modify its data.
               You can create an image manually and convert it to a
               pixel matrix and vice-versa.
*/

#include "pixelMap.h"
#include <fstream>
#include <iostream>
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/glut.h>

//////////////////////////////////////////////////////////////////
PixelMap::PixelMap(const char *fname)
	: width(0), height(0), data(0)
{
   this->grayScale = false;
	this->Read(fname);
}

//////////////////////////////////////////////////////////////////
PixelMap::PixelMap()
	: width(0), height(0), data(0)
{
   this->grayScale = false;
}

//////////////////////////////////////////////////////////////////
PixelMap::~PixelMap()
{
	if( data )
		delete[] data;
}

//////////////////////////////////////////////////////////////////
void PixelMap::Read(const char *fname)
{
	using namespace std;

	unsigned short planes;	// number of planes in image (must be 1)
	unsigned short bpp;			// number of bits per pixel (must be 24)

	ifstream fin(fname, ios::in | ios::binary);
	if( !fin )
	{
		cerr << "File not found " << fname << '\n';
		//////exit(1);
	}

	fin.seekg(18, ios::cur);

	fin.read((Byte *)&width, sizeof(int));
	fin.read((Byte *)&height, sizeof(int));
	cout << "width: " << width << " height: " << height << '\n';

	fin.read((Byte *)&planes, sizeof(unsigned short));
	if( planes != 1 )
	{
		cout << "Planes from " << fname << " is not 1: " << planes << "\n";
		//////exit(1);
	}

	fin.read((Byte *)&bpp, sizeof(unsigned short));
	if( bpp != 24 )
	{
		cout << "Bpp from " << fname << " is not 24: " << bpp << "\n";
		////exit(1);
	}

	fin.seekg(24, ios::cur);

	int size(width * height * 3); // size of the image in bytes (3 is to RGB component).
	data = new uByte[size];
	fin.read((Byte *)data, size);

	uByte tmp;	// temporary color storage for bgr-rgb conversion.

	for( int i(0); i <  size; i += 3 )
	{
		tmp = data[i];
		data[i] = data[i+2];
		data[i+2] = tmp;
	}
}

//////////////////////////////////////////////////////////////////
void PixelMap::CreateImage(int w, int h, uByte *d)
{
   this->width  = w;
   this->height = h;
   this->data   = d;
}

//////////////////////////////////////////////////////////////////
void PixelMap::ViewImage()
{
   glDrawPixels(this->GetWidth(), this->GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, (uByte*) this->GetData());
}

//////////////////////////////////////////////////////////////////
uByte PixelMap::pixel_elem(int x, int y, int elem)
{
	int pos = (y*width+x) * 3 + elem;
	return data[pos];
}

//////////////////////////////////////////////////////////////////
uByte *PixelMap::pixel_pos(int x, int y)
{
	int pos = (y * width + x) * 3;
	return &data[pos];
}

//////////////////////////////////////////////////////////////////
int PixelMap::GetWidth()
{
   return this->width;
}

//////////////////////////////////////////////////////////////////
void PixelMap::SetWidth(int w)
{
   this->width = w;
}

//////////////////////////////////////////////////////////////////
int PixelMap::GetHeight()
{
   return this->height;
}

//////////////////////////////////////////////////////////////////
void PixelMap::SetHeight(int h)
{
   this->height = h;
}

//////////////////////////////////////////////////////////////////
uByte* PixelMap::GetData()
{
   return this->data;
}

//////////////////////////////////////////////////////////////////
void PixelMap::SetData(uByte *d)
{
   this->data = d;
}

//////////////////////////////////////////////////////////////////
void PixelMap::ConvertPixelsToData(pixel **m)
{
   int k = 0;
   for(int j = 0; j < this->GetHeight(); j++)
      for(int i = 0; i < this->GetWidth(); i++)
      {
         // If grayscale, set all components to value
         if(this->grayScale)
            m[i][j].R = m[i][j].G = m[i][j].B = m[i][j].value;
         data[k]   = m[i][j].R;
         data[k+1] = m[i][j].G;
         data[k+2] = m[i][j].B;
         k+=3;
      }
}

//////////////////////////////////////////////////////////////////
void PixelMap::ConvertDataToPixels(pixel **m)
{
   uByte *dataAux = this->data;
   for(int j = 0; j < this->GetHeight(); j++)
      for(int i = 0; i < this->GetWidth(); i++)
      {
         if(this->grayScale)
            m[i][j].value = *dataAux; // All components are the same in grayscale mode
         m[i][j].R = *dataAux++;
         m[i][j].G = *dataAux++;
         m[i][j].B = *dataAux++;
      }
}

//////////////////////////////////////////////////////////////////
void PixelMap::ConvertToGrayScale()
{
   this->grayScale = true;
   double value;
	int k = 0;
   for(int j = 0; j < this->GetHeight(); j++)
      for(int i = 0; i < this->GetWidth(); i++)
      {
         value =  0.3 * (double) data[k] + 0.59 * (double) data[k+1] + 0.11 * (double) data[k+2];
         data[k] = data[k+1] = data[k+2] = (uByte) value;
         k+=3;
      }
}
